package com.matrix.async.core;

import java.util.concurrent.locks.ReentrantLock;

import org.apache.log4j.Logger;

public class AsyncProcessPlatform implements Controllable {
	private Logger log = Logger.getLogger(AsyncProcessPlatform.class);
	private static final AsyncProcessPlatform app = new AsyncProcessPlatform();

	// 组件仓库
	private static final ComponentRepository queues = new ComponentRepository();
	private static final ComponentRepository producers = new ComponentRepository();
	private static final ComponentRepository comsumers = new ComponentRepository();
	private static final ComponentRepository exceptionHandlers = new ComponentRepository();
	private static final ComponentRepository retryThreads = new ComponentRepository();

	// 组件仓库（根据任务类型打包好的组件仓库）

	private static final BundleRepository bundles = new BundleRepository();

	protected final ReentrantLock lock = new ReentrantLock();

	private AsyncProcessPlatform() {
	}

	public static AsyncProcessPlatform getInstance() {
		return app;
	}
	
	/**
	 * 组件分组
	 * 
	 * @param component
	 * @return
	 */
	public boolean bindComponent(APPComponent component) {
		if (component instanceof APPQueue) {
			return queues.bind(component);
		} else if (component instanceof Producer) {
			return producers.bind(component);
		} else if (component instanceof Comsumer) {
			return comsumers.bind(component);
		} else if (component instanceof ExceptionHandler) {
			return exceptionHandlers.bind(component);
		} else if (component instanceof RetryThread) {
			return retryThreads.bind(component);
		} else {
			throw new RuntimeException("Unknow APPCompoment with name" + component.getName());
		}
	}

	public static ComponentRepository getQueues() {
		return queues;
	}

	/*
	 * 打包相关组件
	 */
	public boolean bundleComponent(APPComponent component) {
		APPBundle bundle = null;
		if ((bundle = bundles.lookup(component.getName())) == null) {
			bundle = new APPBundle(component.getTaskType());
		}

		if (component instanceof APPQueue) {
			bundle.setTaskQueue((APPQueue) component);
		} else if (component instanceof Producer) {
			bundle.setProducer((Producer) component);
		} else if (component instanceof Comsumer) {
			bundle.addConsumers((Comsumer) component);
		} else if (component instanceof ExceptionHandler) {
			bundle.setExceptionHandler((ExceptionHandler) component);
		} else if (component instanceof RetryThread) {
			bundle.setRetryThread((RetryThread) component);
		}
		return bundles.bind(bundle);
	}

	@Override
	public void start() {
		final ReentrantLock lock = this.lock;
		lock.lock();
		try {
			for (APPComponent workThread : producers.lockupAll()) {
				startDelay((WorkThread) workThread);
			}

			for (APPComponent workThread : comsumers.lockupAll()) {
				startDelay((WorkThread) workThread);
			}

			for (APPComponent workThread : exceptionHandlers.lockupAll()) {
				startDelay((WorkThread) workThread);
			}

		} finally {
			lock.unlock();
		}
	}

	public void startDelay(final WorkThread thread) {
		if ("true".equals(thread.getBeanUse())) {
			if (thread.getStartupDelay() <= 0) {
				thread.start();
			} else {
				try {
					new Thread() {
						public void run() {
							try {
								Thread.sleep(thread.getStartupDelay() * 1000);
							} catch (InterruptedException e) {
								log.error(e.getMessage(), e);
							}
							thread.start();
						};
					}.start();
				} catch (Exception e) {
					log.error(e.getMessage(), e);
				}
			}
			log.info("WorkThread " + thread.getName() + " in running state");
		} else {
			log.info("WorkThread " + thread.getName() + " is request don't start");
		}

	}

	@Override
	public void suspend() {
		final ReentrantLock lock = this.lock;
		lock.lock();
		try {
			for (APPComponent workThread : producers.lockupAll()) {
				((WorkThread) workThread).suspend();
			}

			for (APPComponent workThread : comsumers.lockupAll()) {
				((WorkThread) workThread).suspend();
			}

			for (APPComponent workThread : exceptionHandlers.lockupAll()) {
				((WorkThread) workThread).suspend();
			}

		} finally {
			lock.unlock();
		}

	}

	/**
	 * 恢复所有的组件
	 */
	@Override
	public void resume() {
		final ReentrantLock lock = this.lock;
		lock.lock();
		try {
			for (APPComponent workThread : producers.lockupAll()) {
				((WorkThread) workThread).resume();
			}

			for (APPComponent workThread : comsumers.lockupAll()) {
				((WorkThread) workThread).resume();
			}

			for (APPComponent workThread : exceptionHandlers.lockupAll()) {
				((WorkThread) workThread).resume();
			}

		} finally {
			lock.unlock();
		}

	}

	@Override
	public void shutdownNow() {
		final ReentrantLock lock = this.lock;
		lock.lock();
		try {
			for (APPComponent workThread : producers.lockupAll()) {
				((WorkThread) workThread).shutdownNow();
			}

			for (APPComponent workThread : comsumers.lockupAll()) {
				((WorkThread) workThread).shutdownNow();
			}

			for (APPComponent workThread : exceptionHandlers.lockupAll()) {
				((WorkThread) workThread).shutdownNow();
			}

		} finally {
			lock.unlock();
		}

	}

	@Override
	public void shutdown() {
		final ReentrantLock lock = this.lock;
		lock.lock();
		try {
			for (APPComponent workThread : producers.lockupAll()) {
				((WorkThread) workThread).shutdown();
			}

			for (APPComponent workThread : comsumers.lockupAll()) {
				((WorkThread) workThread).shutdown();
			}

			for (APPComponent workThread : exceptionHandlers.lockupAll()) {
				((WorkThread) workThread).shutdown();
			}

		} finally {
			lock.unlock();
		}

	}
	
	public static ComponentRepository getProducers() {
		return producers;
	}

	public static ComponentRepository getComsumers() {
		return comsumers;
	}
	
	public boolean unBindComponent(APPComponent component) {
		if (component instanceof APPQueue) {
			return queues.remove(component.getName());
		} else if (component instanceof Producer) {
			return producers.remove(component.getName());
		} else if (component instanceof Comsumer) {
			return comsumers.remove(component.getName());
		} else if (component instanceof ExceptionHandler) {
			return exceptionHandlers.remove(component.getName());
		} else if (component instanceof RetryThread) {
			return retryThreads.remove(component.getName());
		} else {
			throw new RuntimeException("Unknow APPCompoment with name" + component.getName());
		}
	}
	
}
